package main

import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"smart-flow/agent/proto"
	"smart-flow/agent/sys"
	"smart-flow/agent/transform"
	"strconv"
	"strings"
	"syscall"
	"time"
)

const (
	AgentVer = 120
)

var (
	agentId    string
	infoUrl    string
	duration   int64
	rateLimit  int64
	needV6     int
	proveV6Url string
	pidFile    string // PID文件路径
	quit       chan os.Signal
)

func init() {
	flag.StringVar(&agentId, "i", "", "Agent唯一标识")
	flag.StringVar(&infoUrl, "m", "", "管理地址")
	flag.Int64Var(&duration, "d", 0, "执行时间(单位 分钟)")
	flag.Int64Var(&rateLimit, "r", 0, "限速速率(Kbps)")
	flag.IntVar(&needV6, "v", 0, "使用IpV6")
	flag.StringVar(&proveV6Url, "p", "", "IpV6探测地址")
	pidFile = filepath.Join(os.TempDir(), "sfa.pid")
}

func main() {
	flag.Parse()               // 无论主进程还是子进程，都需要调用 flag.Parse() 来解析命令行参数
	nonFlagArgs := flag.Args() // 返回的是非标志参数（non-flag arguments）我们约定最后一个非标志参数为 "child" 时，表示这是一个子进程
	if len(nonFlagArgs) > 0 && nonFlagArgs[len(nonFlagArgs)-1] == "child" {
		childProcess()
	} else {
		mainProcess()
	}
}

func mainProcess() {
	handleArguments() // 调用函数显示并处理这些参数
	if isChildProcessRunning() {
		fmt.Println("child-running")
		return
	}
	if needV6 == 1 && proveV6Url != "" {
		if !checkReachable(proveV6Url) {
			fmt.Println("child-unreachable")
			return
		}
	}
	startBackgroundChildProcess()
}

func handleArguments() {
	if strings.TrimSpace(agentId) == "" {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
	if strings.TrimSpace(infoUrl) == "" {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
	if duration < 1 || duration > 1440 {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
	if rateLimit < 100 {
		fmt.Println("Hello Android!")
		os.Exit(2)
	}
}

func startBackgroundChildProcess() {
	args := []string{
		"-i", agentId,
		"-m", infoUrl,
		"-d", strconv.FormatInt(duration, 10),
		"-r", strconv.FormatInt(rateLimit, 10),
		"-v", strconv.Itoa(needV6),
		"child",
	}
	cmd, err := sys.Daemonize(args)
	if err != nil {
		panic("Failed to start background process: " + err.Error())
	}
	err = cmd.Process.Release()
	if err != nil {
		panic("Failed to release child process: " + err.Error())
	}
}

func childProcess() {
	fmt.Printf("child-started-%d\n", os.Getpid())
	_ = createPIDFile()
	defer removePIDFile()
	setupSignalHandlers() // 设置信号处理，确保在收到终止信号时也能删除PID文件
	infoResp := handleInfo(proto.AgentInfo{
		AgentId:          agentId,
		AgentVer:         AgentVer,
		AgentTime:        time.Now().Unix(),
		AgentRateLimit:   rateLimit,
		AgentV6Reachable: needV6,
	})
	if infoResp == nil {
		shutdown()
	}
	speed := transform.SpeedTest(infoResp.SpeedUrl, infoResp.SpeedDuration)
	loginResp := handleLogin(proto.AgentInfo{
		AgentId:          agentId,
		AgentVer:         AgentVer,
		AgentTime:        time.Now().Unix(),
		AgentRateLimit:   rateLimit,
		AgentV6Reachable: needV6,
	}, proto.BandwidthInfo{
		Bandwidth:    speed,
		RealTcpRate:  0,
		RealHttpRate: 0,
		RealUdpRate:  0,
	}, infoResp.LoginUrl)
	if loginResp == nil {
		shutdown()
	}
	sender := new(transform.DataSender).Init(loginResp, 2*transform.MB, shutdown)
	if sender == nil {
		shutdown()
	}
	sender.Start()
	serverTime := loginResp.Timestamp
	endTime := loginResp.Timestamp + duration*60
	interval := time.Duration(loginResp.AgentInterval) * time.Second
	for {
		time.Sleep(interval)
		aliveResp := handleAlive(proto.AgentInfo{
			AgentId:          agentId,
			AgentVer:         AgentVer,
			AgentTime:        time.Now().Unix(),
			AgentRateLimit:   rateLimit,
			AgentV6Reachable: needV6,
		}, proto.BandwidthInfo{
			Bandwidth:    speed,
			RealTcpRate:  0,
			RealHttpRate: 0,
			RealUdpRate:  0,
		}, proto.RunTimeInfo{
			SrvLastTime: serverTime,
			EndTime:     endTime,
			DebugInfo:   "",
		}, infoResp.AliveUrl)
		if aliveResp == nil {
			sender.Stop()
			shutdown()
		}
		serverTime = aliveResp.Timestamp
		if serverTime >= endTime {
			sender.Stop()
			shutdown()
		}
		if aliveResp.NeedUpdateRate == 1 {
			sender.Pause()
			sender.UpdatePerUseCount(aliveResp.PerUseCount)
			sender.UpdateRates(aliveResp.TcpRateLimit, aliveResp.UdpRateLimit, aliveResp.HttpRateLimit)
			sender.Resume()
		}
	}
}

func checkReachable(url string) bool {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()
	return doCheckReachable(ctx, url)
}

func doCheckReachable(ctx context.Context, url string) bool {
	client := http.Client{
		Timeout: time.Second * 3,
	}
	req, err := http.NewRequestWithContext(ctx, "HEAD", url, nil)
	if err != nil {
		return false
	}
	resp, err := client.Do(req)
	if err != nil {
		return false
	}
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(resp.Body)
	return true
}

func handleRequest(body []byte, url string) []byte {
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(body))
	if err != nil || resp == nil {
		return nil
	}
	defer func(resp *http.Response) {
		if resp != nil && resp.Body != nil {
			_ = resp.Body.Close()
		}
	}(resp)
	if resp.StatusCode != http.StatusOK {
		return nil
	}
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil
	}
	// fmt.Printf("获取服务器信息成功, 数据:%s\n", string(respBody))
	return respBody
}

func handleInfo(agentInfo proto.AgentInfo) *proto.InfoResp {
	reqBody := proto.InfoReq{
		AgentInfo: agentInfo,
	}
	body, _ := json.Marshal(reqBody)
	respBody := handleRequest(body, infoUrl)
	if respBody == nil {
		return nil
	}
	data := new(proto.InfoResp)
	err := json.Unmarshal(respBody, data)
	if err != nil {
		return nil
	}
	return data
}

func handleLogin(agentInfo proto.AgentInfo, bandwidthInfo proto.BandwidthInfo, url string) *proto.LoginResp {
	reqBody := proto.LoginReq{
		AgentInfo:     agentInfo,
		BandwidthInfo: bandwidthInfo,
	}
	body, _ := json.Marshal(reqBody)
	respBody := handleRequest(body, url)
	if respBody == nil {
		return nil
	}
	data := new(proto.LoginResp)
	err := json.Unmarshal(respBody, data)
	if err != nil {
		return nil
	}
	return data
}

func handleAlive(agentInfo proto.AgentInfo, bandwidthInfo proto.BandwidthInfo, runtimeInfo proto.RunTimeInfo, url string) *proto.AliveResp {
	reqBody := proto.AliveReq{
		AgentInfo:     agentInfo,
		BandwidthInfo: bandwidthInfo,
		RunTimeInfo:   runtimeInfo,
	}
	body, _ := json.Marshal(reqBody)
	respBody := handleRequest(body, url)
	if respBody == nil {
		return nil
	}
	data := new(proto.AliveResp)
	err := json.Unmarshal(respBody, data)
	if err != nil {
		return nil
	}
	return data
}

func isChildProcessRunning() bool {
	pidBytes, err := os.ReadFile(pidFile) // 读取PID文件
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
		// fmt.Printf("读取PID文件失败: %v\n", err)
		return false
	}
	pidStr := strings.TrimSpace(string(pidBytes))
	pid, err := strconv.Atoi(pidStr)
	if err != nil {
		// fmt.Printf("解析PID失败: %v\n", err)
		removePIDFile()
		return false
	}
	process, err := os.FindProcess(pid)
	if err != nil {
		removePIDFile()
		return false
	}
	if runtime.GOOS != "windows" {
		err = process.Signal(syscall.Signal(0)) // 发送信号0来检查进程是否真的存在
		if err != nil {
			removePIDFile()
			return false
		}
	}
	return true
}

func createPIDFile() error {
	pid := os.Getpid()
	pidStr := strconv.Itoa(pid)
	return os.WriteFile(pidFile, []byte(pidStr), 0644)
}

func removePIDFile() {
	_ = os.Remove(pidFile)
}

func setupSignalHandlers() {
	quit = make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-quit
		// fmt.Println("收到终止信号，Agent清理资源并关闭...")
		removePIDFile()
		os.Exit(0)
	}()
}

func shutdown() {
	quit <- syscall.SIGTERM
	time.Sleep(5 * time.Second)
	os.Exit(0)
}
